package com.jacky.ws.server;

import com.jacky.ws.handler.HandshakeWebSocketHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Component;

/**
 * https://blog.csdn.net/u013771019/article/details/139162067
 */
@Component
@ConditionalOnExpression("'${webSocket.run}'.equals('annotation')")
public class HandshakeWebSocketServer implements ApplicationRunner {

    private final Logger LOG = LoggerFactory.getLogger(HandshakeWebSocketServer.class);
    @Value("${webSocket.annotation.port:8090}")
    private Integer serverPort;
    @Value("${webSocket.annotation.websocket-path:ws://127.0.0.1:8090/anno-ws}")
    private String webSocketPath;

    private void startServer() {
        LOG.info("正在启动webSocket服务器");
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workGroup = new NioEventLoopGroup();
        try {

            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workGroup);
            bootstrap.channel(NioServerSocketChannel.class);
            bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    // 设置log监听器,并且日志级别为debug
                    ch.pipeline().addLast("logging", new LoggingHandler("DEBUG"));
                    // 设置解码器
                    ch.pipeline().addLast("http-codec", new HttpServerCodec());
                    // 聚合器,使用webSocket会用到
                    ch.pipeline().addLast("aggregator", new HttpObjectAggregator(65535));
                    // 用于大数据的分区传输
                    ch.pipeline().addLast("http-chunked", new ChunkedWriteHandler());
                    // 自定义的型业务handler, 这里处理webSocket建立请求和消息发送请求
                    ch.pipeline().addLast("handler", new HandshakeWebSocketHandler(webSocketPath));
                }
            });
            Channel channel = bootstrap.bind(serverPort).sync().channel();
            LOG.info("webSocket服务器启动成功:{}", channel);
            channel.closeFuture().sync();
        } catch (InterruptedException e) {
            LOG.error("webSocket运行出错:", e);
        } finally {
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
            LOG.info("webSocket服务器已关闭");
        }
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        LOG.info("annotation webSocket server port:{}", serverPort);
        this.startServer();
    }
}
